﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Xceed.Silverlight.ListBox.PositioningFunction;

namespace Xceed.Silverlight.ListBox
{
  public sealed class StackPanel : PanelBase
  {
    #region Constructors

    public StackPanel()
    {
      this.PositioningFunction = new VerticalPositioning();
    }

    #endregion

    #region Orientation Property

    public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
      "Orientation",
      typeof( Orientation ),
      typeof( StackPanel ),
      new PropertyMetadata(
        Orientation.Vertical,
        new PropertyChangedCallback( StackPanel.OnOrientationChanged ) ) );

    public Orientation Orientation
    {
      get
      {
        return ( Orientation )this.GetValue( StackPanel.OrientationProperty );
      }
      set
      {
        this.SetValue( StackPanel.OrientationProperty, value );
      }
    }

    private static void OnOrientationChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      PanelBase panel = sender as PanelBase;
      Orientation orientation = ( Orientation )e.NewValue;

      if( ( orientation ) == Orientation.Vertical )
      {
        panel.PositioningFunction = new VerticalPositioning();
      }
      else if( orientation == Orientation.Horizontal )
      {
        panel.PositioningFunction = new HorizontalPositioning();
      }
    }

    #endregion

    #region ItemOffset Property

    internal override double ItemOffset
    {
      get
      {
        return ( ( this.Orientation == Orientation.Vertical ) ?
          this.VerticalOffset :
          this.HorizontalOffset );
      }
    }

    #endregion

    #region ItemsInViewport Property

    internal override double ItemsInViewport
    {
      get
      {
        return ( ( this.Orientation == Orientation.Vertical ) ? this.ViewportHeight : this.ViewportWidth );
      }
    }

    #endregion

    internal override void ForceItemOffset( double offset )
    {
      if( this.Orientation == Orientation.Vertical )
      {
        this.ForceVerticalOffset( offset );
      }
      else
      {
        this.ForceHorizontalOffset( offset );
      }
    }

    protected override Size ArrangeOverride( Size finalSize )
    {
      if( this.LayoutManager.IsLayoutInhibited )
      {
        this.ArrangeOverrideInhibited = true;
        return finalSize;
      }

      Point emptyPoint = new Point();

      if( this.Orientation == Orientation.Vertical )
      {
        foreach( ListBoxContainer container in this.Children )
        {
          double maxHeight = container.Configuration.CachedSize.Height;

          container.Arrange( new Rect( emptyPoint, new Size( finalSize.Width, maxHeight ) ) );
        }
      }
      else
      {
        foreach( ListBoxContainer container in this.Children )
        {
          double maxWidth = container.Configuration.CachedSize.Width;

          container.Arrange( new Rect( emptyPoint, new Size( maxWidth, finalSize.Height ) ) );
        }
      }

      RectangleGeometry clip = this.Clip as RectangleGeometry;

      if( clip == null )
        clip = new RectangleGeometry();

      clip.Rect = new Rect( emptyPoint, finalSize );
      this.Clip = clip;

      return finalSize;
    }

    internal override double GetConfigurationDistanceUsedForExtent( ListBoxContainerConfigurationBase configuration )
    {
      if( this.Orientation == Orientation.Vertical )
      {
        return configuration.PrepareSize.Height;
      }
      else
      {
        return configuration.PrepareSize.Width;
      }
    }

    internal override void OnScrollManagerActualScrollIndexChanged( object sender, EventArgs e )
    {
      if( this.Orientation == Orientation.Vertical )
      {
        this.SetVerticalOffsetCore( this.ScrollManager.ActualScrollIndex );

        this.ScrollManager.UpdateAnimationSettings( this.ExtentHeight - this.ViewportHeight );
      }
      else
      {
        this.SetHorizontalOffsetCore( this.ScrollManager.ActualScrollIndex );

        this.ScrollManager.UpdateAnimationSettings( this.ExtentWidth - this.ViewportWidth );
      }
    }

    internal override double GetAverageContainerSize()
    {
      if( this.Orientation == Orientation.Vertical )
      {
        return this.ViewportSizeInPixels.Height / this.ItemsInViewport;
      }
      else
      {
        return this.ViewportSizeInPixels.Width / this.ItemsInViewport;
      }
    }

    internal override ContainerLayoutMode GetPositioningContainerLayoutMode()
    {
      if( this.Orientation == Orientation.Vertical )
      {
        return ContainerLayoutMode.Vertical;
      }
      else
      {
        return ContainerLayoutMode.Horizontal;
      }
    }

    internal override Orientation GetScrollingOrientation()
    {
      return this.Orientation;
    }

    internal override AddingItemTransitionAnimation CreateAddingItemTransitionAnimation()
    {
      AddingItemTransitionAnimation animation = base.CreateAddingItemTransitionAnimation();
      animation.Orientation = this.Orientation;
      return animation;
    }

    internal override RemovingItemTransitionAnimation CreateRemovingItemTransitionAnimation()
    {
      RemovingItemTransitionAnimation animation = base.CreateRemovingItemTransitionAnimation();
      animation.Orientation = this.Orientation;
      return animation;
    }

    internal override TransitionAnimation CreateScrollingUpTransitionAnimation()
    {
      ScrollingUpTransitionAnimation animation = new ScrollingUpTransitionAnimation( this.ParentListBox.AnimationSettings );
      animation.Orientation = this.Orientation;
      return animation;
    }

    internal override TransitionAnimation CreateScrollingDownTransitionAnimation()
    {
      ScrollingDownTransitionAnimation animation = new ScrollingDownTransitionAnimation( this.ParentListBox.AnimationSettings );
      animation.Orientation = this.Orientation;
      return animation;
    }

    internal override void ProcessMouseWheel( MouseWheelEventArgs e )
    {
      if( this.Orientation == Orientation.Vertical )
      {
        if( e.Delta < 0 )
        {
          base.MouseWheelDown();
        }
        else
        {
          base.MouseWheelUp();
        }
      }
      else
      {
        if( e.Delta < 0 )
        {
          base.MouseWheelRight();
        }
        else
        {
          base.MouseWheelLeft();
        }
      }
    }

    #region OffsetCore methods

    internal override void SetHorizontalOffsetCore( double offset )
    {
      offset = Math.Min( offset, this.ExtentWidth - this.ViewportWidth );
      offset = Math.Max( 0, offset );

      if( offset != this.HorizontalOffset )
      {
        if( this.Orientation == Orientation.Horizontal )
        {
          long newValue = ( long )offset;
          long oldValue = ( long )this.HorizontalOffset;
          long moveDifference = newValue - oldValue;

          if( moveDifference != 0 )
            this.EnumeratorHelper.Move( moveDifference );
        }

        this.HorizontalOffset = offset;

        if( this.ForceInvalidateScrollInfo )
        {
          this.InvalidateScrollInfo();
        }

        this.LayoutManager.UpdateViewportContent( new Size( this.ActualWidth, this.ActualHeight ) );
      }
    }

    internal override void SetVerticalOffsetCore( double offset )
    {
      // Correct the offset to ensure it is in a valid range for IScrollInfo
      offset = Math.Min( offset, this.ExtentHeight - this.ViewportHeight );
      offset = Math.Max( 0, offset );

      if( offset != this.VerticalOffset )
      {
        if( this.Orientation == Orientation.Vertical )
        {
          long newValue = ( long )offset;
          long oldValue = ( long )this.VerticalOffset;
          long moveDifference = newValue - oldValue;

          if( moveDifference != 0 )
          {
            this.EnumeratorHelper.Move( moveDifference );
          }
        }

        this.VerticalOffset = offset;

        if( this.ForceInvalidateScrollInfo )
        {
          this.InvalidateScrollInfo();
        }

        this.LayoutManager.UpdateViewportContent( this.RenderSize );
      }
    }

    #endregion
  }
}
